package top.scauwlt.extension;

import lombok.extern.slf4j.Slf4j;
import top.scauwlt.utils.StringUtil;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * refer to dubbo spi: https://dubbo.apache.org/zh/docs/v2.7/dev/source/dubbo-spi/
 *
 * @Author wmm
 * @Date 2022/7/21 17:06
 */
@Slf4j
public final class ExtensionLoader<T> {
    /**
     * 默认服务接口实现文件路径
     */
    private static final String SERVICE_PATH = "META-INF/extensions/";

    /**
     * 加载器的缓存，属于类
     * key：接口的class对象
     * value：该接口服务的ExtensionLoader
     */
    private static final Map<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();

    /**
     * 实现类实例的缓存，属于类
     * key: 接口实现类的class对象
     * value：接口服务实现类对象
     */
    private static final Map<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();

    /**
     * 当前加载器对应的接口类型，属于加载器实例
     * 服务接口的Class对象
     */
    private final Class<?> type;

    /**
     * Holder的缓存，属于加载器实例
     * Holder的作用：在函数传递中保证引用能改变
     * key: 服务实现类名字（别称） eg: zk 、 kryo
     * value: 该服务实现类的Holder
     */
    private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

    /**
     * 属于加载器实例
     * Holder 里放的Map
     * key: 服务实现类名字（别称） eg: zk 、 kryo
     * value: 该服务实现类的Holder
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();


    private ExtensionLoader(Class<?> type) {
        this.type = type;
    }

    /**
     * 获取这个接口的插件加载器
     *
     * @param type 接口的class eg:Serializer.class
     * @param <S>  接口的类型
     */
    public static <S> ExtensionLoader<S> getExtensionLoader(Class<S> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type should not be null!");
        } else if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type must be interface!");
        } else if (type.getAnnotation(SPI.class) == null) {
            throw new IllegalArgumentException("Extension type must be annotated by @SPI" + type.getName());
        }
        ExtensionLoader<S> extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        if (extensionLoader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<>(type));
            extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        }
        return extensionLoader;
    }

    /**
     * 获取名字为name 对应的 Extension实现类 eg: kryo / hessian / protostuff
     *
     * @param name
     * @return
     */
    public T getExtension(String name) {
        if (StringUtil.isBlank(name)) {
            throw new IllegalArgumentException("Extension name should not be null or empty!");
        }
        // 先拿到Holder
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        // 再从Holder里拿出instance，没有就去建一个
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                }
            }
        }
        return (T) instance;
    }

    /**
     * 查看 EXTENSION_INSTANCES 中是否有名字为name的实现类对象，没有则用其class创建一个
     *
     * @param name
     * @return
     */
    private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw new IllegalArgumentException("no such extension of name " + name);
        }
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            try {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        return instance;
    }

    /**
     * 返回当前ExtensionLoader对应的所有实现类，如 ExtensionLoader<Serializer>
     * 则 返回 [(kryo,kryo.class),(hessian,hessian.class)...]
     *
     * @return
     */
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = new HashMap<>();
                    loadDirectory(classes);
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

    /**
     * 读取目录下所有实现类的class对象并填充
     *
     * @param classes
     */
    private void loadDirectory(Map<String, Class<?>> classes) {
        String path = SERVICE_PATH + type.getName();
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> urls;
            // 获取顺序：优先本应用，然后按照MATE-INF/MANIFEST.MF中jar的顺序进行查找。
            urls = classLoader.getResources(path);
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    loadResource(classes, classLoader, url);
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }

    }

    /**
     * 用类加载器加载url对应文件中的实现类
     *
     * @param classes
     * @param classLoader
     * @param url
     */
    private void loadResource(Map<String, Class<?>> classes, ClassLoader classLoader, URL url) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    // 截取注释前的内容
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        final int ei = line.indexOf('=');
                        String name = line.substring(0, ei).trim();
                        String clazzName = line.substring(ei + 1).trim();
                        //第一次查找到就不往下继续找，目的是能让用户覆盖具体实现
                        if (name.length() > 0 && clazzName.length() > 0 && !classes.containsKey(name)) {
                            Class<?> clazz = classLoader.loadClass(clazzName);
                            classes.put(name, clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}
